Skip to content

Comments

feat: Version 2.0.0 - 重构、CI/CD 和增强功能#9

Merged
piratf merged 42 commits intomainfrom
feat-lint-test
Jan 25, 2026
Merged

feat: Version 2.0.0 - 重构、CI/CD 和增强功能#9
piratf merged 42 commits intomainfrom
feat-lint-test

Conversation

@piratf
Copy link
Owner

@piratf piratf commented Jan 24, 2026

Summary

  • 核心功能: 添加模糊路径解析功能,支持未加引号的含空格路径(如 C:\My Documents
  • 项目架构重构:采用分层设计(CLI、Core、Storage、Utils)
  • 完善 CI/CD 流程:添加 GitHub Actions 自动化测试和发布
  • 测试框架现代化:从 unittest.mock 迁移到 pyfakefs,139 个测试全部通过
  • 增强代码质量:配置 pre-commit hooks、类型检查、代码格式化

主要变更

新功能: 模糊路径解析

当用户输入未加引号的含空格路径时(如 C:\My Documents),工具会:

  1. 智能分析参数序列,重建完整路径
  2. 提供候选路径列表供用户选择(键盘交互菜单)
  3. 自动区分文件和文件夹,跳过不可选项
  4. 正确解析备注内容
# 之前必须使用引号
windows-folder-remark "C:\My Documents" "备注内容"

# 现在可以省略引号
windows-folder-remark C:\My Documents 备注内容

项目重构

  • 迁移到 pyproject.toml 进行项目配置和版本管理
  • 重构目录结构为分层架构:cli/, core/, storage/, utils/
  • 使用 argparse 替代原有命令行参数解析
  • path_resolver.py 使用 Path 对象替代字符串路径
  • 统一代码换行符为 LF

CI/CD

  • 添加 GitHub Actions 工作流:
    • test.yml: 自动运行单元测试、集成测试和类型检查
    • pull_request.yml: PR 验证工作流
    • release.yml: 自动构建和发布 PyInstaller 单文件 exe
  • 配置 pre-commit hooks(ruff format, ruff check, mypy)

测试改进

  • unittest.mock 迁移到 pyfakefs 进行文件系统模拟
  • 139 个测试全部通过(单元测试 + 集成测试 + Windows 测试)
  • 添加 pytest_configure 钩子强制 UTF-8 编码,支持 emoji 输出

Release 脚本增强

  • 添加分支检查(必须在 main 分支)
  • 添加工作目录干净检查
  • 添加远程同步检查
  • 添加版本号递增验证
  • --push 自动启用 --commit

其他增强

  • 编码检测与修复: --view 命令自动检测 desktop.ini 编码,非 UTF-16 时提示用户修复
  • 本地打包脚本: python -m scripts.build 一键打包 exe
  • 删除备注功能: --delete 命令
  • 备注长度验证: 最大 260 字符
  • 安全性改进: 使用 subprocess 替代 os.system 防止命令注入

Test plan

  • 单元测试通过(139 个测试)
  • 代码格式检查通过(ruff)
  • 类型检查通过(mypy)
  • 本地打包成功
  • PR 合并后 CI/CD 流程验证

piratf and others added 30 commits January 24, 2026 12:25
pyinstaller 的 --onefile 参数虽然生成单个 exe 文件很好看,但是会导致 windows defender 报毒,所以就没有打包成单个文件了。本身 onefile 生成的也是一个自解压包,用起来是一样的。
将 get_setting_file_path() 函数中的字符串拼接改为 os.path.join(),提高路径处理的安全性和跨平台兼容性。
在 open() 函数中明确指定 encoding=defEncoding,确保文件编码与系统编码一致,避免编码不一致导致的问题。
在 sys_encode() 函数中添加 try-except 块,捕获 UnicodeEncodeError 和 UnicodeDecodeError,防止程序因编码问题崩溃。
将 run_command() 函数从 os.system() 改为 subprocess.call(),添加错误处理和返回值检查,提高安全性和可靠性。
为 update_folder_comment() 和 add_comment() 添加异常处理,包括 IOError 捕获、命令执行结果检查和失败提示。同时在路径处理中添加 .strip() 去除首尾空格。
添加 MAX_COMMENT_LENGTH 常量和备注长度检查,当备注超过 260 个字符时进行截断并提示用户,避免超出 Windows InfoTip 长度限制。
添加 check_platform() 函数在程序启动时检查操作系统,如果不是 Windows 系统则显示错误信息并退出,避免在非 Windows 系统上产生误导性错误。
改进命令行帮助信息,提供更清晰的使用说明和示例。在交互模式启动时显示工具名称和退出提示,提升用户体验。
新增 delete_folder_comment() 函数和 --delete 命令行选项,允许用户删除文件夹备注。添加 show_help() 函数统一显示帮助信息,参数错误时自动显示帮助。
使用 argparse 库替代手动的 sys.argv 解析,提供更规范、更易维护的命令行参数处理。支持位置参数和可选参数,自动处理参数验证。
按照 Python 最佳实践重构项目结构:

## 架构改进
- 模块化设计:core(核心功能)、cli(命令行)、utils(工具)
- 使用抽象基类定义统一接口
- 支持文件(NTFS ADS)和文件夹(desktop.ini)备注
- 分离关注点,提高可维护性

## 新功能
- 新增文件备注支持(使用 NTFS 替代数据流)
- 自动识别文件/文件夹类型
- 统一的备注管理接口

## 项目结构
- 新增 remark/storage 模块,封装 desktop.ini 交互逻辑
- 使用 UTF-16 编码写入 desktop.ini(自动添加 BOM)
- 符合 Microsoft 官方文档要求,支持中文等非 ASCII 字符正确显示
- 简化 FolderCommentHandler,委托给 DesktopIniHandler 处理

参考文档:
https://learn.microsoft.com/en-us/windows/win32/shell/how-to-customize-folders-with-desktop-ini
移除文件备注相关的代码和 COM 组件:
- 删除 remark/com/ads_column_handler.py (IColumnProvider COM 组件)
- 删除 remark/storage/property_store.py (Property Store 处理器)
- 删除 remark/core/file_handler.py (文件备注处理器)
- 更新 remark/cli/commands.py 仅处理文件夹
- 更新各模块 __init__.py 移除文件相关导出

原因:IColumnProvider 是已弃用的 Windows XP 接口,现代 Windows 不支持。
要在 Windows 上实现文件备注,需要开发 C++ Property Handler。
主要改进:
1. 删除备注时只移除 InfoTip 行,保留其他设置(IconResource 等)
2. 添加编码检测和转换功能
3. 使用异常处理编码转换取消(更符合 Python 最佳实践)
4. 添加 CLI 入口统一异常处理
5. 移除 notify_shell_update(不需要)

新增 EncodingConversionCanceled 异常类,用于用户拒绝编码转换时。
- 添加 GitHub Actions workflow 自动构建和发布
- 添加 PyInstaller 配置文件
- 添加版本管理脚本
- 添加 CHANGELOG.md 变更日志
- 支持 32 位和 64 位 Windows 构建
- 创建 pyproject.toml 统一管理项目配置
- 删除 setup.py 和 requirements.txt
- 删除 remark/__init__.py 中的 __version__ 变量
- 在 cli/commands.py 添加动态版本读取(importlib.metadata)
- 更新版本管理脚本,只从 pyproject.toml 读取版本
- 添加 ruff、mypy、pytest 配置
- 添加 pre-commit hooks 配置
- 更新 .gitignore 排除构建产物
- 更新 GitHub Actions 工作流
- 移除不必要的 UTF-8 编码声明
- 移除字符串中的 u 前缀(Python 3 不需要)
- 统一导入顺序
- 添加文件末尾换行符
- 更新 ruff 配置忽略中文全角字符警告
- 删除 remark/utils/encoding.py 中的 sys_encode 函数
- 移除所有模块中的 sys_encode 导入
- 将所有 sys_encode() 调用改为直接 print
- 修复嵌套 if 语句 (SIM102)
- 修复异常链问题 (B904)
- 添加 noqa 注释抑制合理的警告
- 优先使用 DESKTOP_INI_ENCODING(utf-16),与写入逻辑保持一致
- 简化异常处理结构,移除不必要的嵌套 try-except
- 改进注释说明编码降级策略的用途
- 合并 test.yml 和 build.yml 为统一的 test-and-build workflow
- 在所有 Python 版本上运行测试,仅在 3.11 上构建 exe
- 更新 release.yml 简化架构配置,添加使用说明
- 添加 pre-push hooks: 自动运行测试和构建 exe
- 统一所有文件使用 LF 换行符
- 添加 .python-version 指定 Python 3.9.22
- 添加 remark.spec 用于 PyInstaller 构建
- 更新 .gitignore 允许提交 .spec 文件
- 更新 pyproject.toml 添加 mypy exclude 配置
- remark.py: 简化文档字符串格式
- CHANGELOG.md: 统一换行符
添加多项发布前检查,防止在错误状态下创建 release tag:
- 分支检查:验证当前分支为主分支
- 工作目录检查:确保无未提交改动
- 远程同步检查:检测本地是否落后于远程
- 版本号递增验证:防止版本回退
- --push/--commit 联动:确保 tag 指向正确的提交

同时添加了完整的单元测试覆盖所有新增功能。
新增功能:
- --view 命令自动检测 desktop.ini 编码,非 UTF-16 时提醒用户
- 支持一键修复编码为 UTF-16 格式
- 新增 scripts/build.py 本地打包脚本
- 在 pyproject.toml 添加 remark-build 入口点

改进:
- remark.spec 禁用 strip(避免无 strip 工具时的警告)
- 打包脚本设置 UTF-8 输出编码
- 更新 README.md 为 2.0.0 版本
- 清理旧的 dist/remark/ 构建目录

测试:新增编码检测和修复的单元测试
piratf added 12 commits January 24, 2026 13:07
- 新增 pull_request.yml 工作流,包含代码检查、测试和打包验证
- 修复 test.yml 中的分支名从 master 更新为 main
避免与 pull_request.yml 重复,职责更清晰:
- test.yml: 仅在 push 时触发
- pull_request.yml: 专门处理 PR 验证
- 分离为 test 和 build-verify 两个 job
- 移除所有 artifact 上传以节省 GitHub 存储配额
- 移除 HTML coverage 报告
- 构建验证仅确认 exe 能正常运行
- 更新 requires-python 为 >=3.11
- 更新 classifiers 移除 3.9 和 3.10,添加 3.13
- 更新 Ruff target-version 为 py311
- 更新 Mypy python_version 为 3.11
- CI 测试矩阵更新为 3.11, 3.12, 3.13
- 构建验证使用 Python 3.11
- 新增 path_resolver 模块,使用 BFS 算法智能重建含空格的完整路径
- 更新 CLI 命令参数解析,支持多位置参数输入
- 添加 pyfakefs 依赖用于文件系统模拟测试
- 添加设置备注成功时的确认提示
- 移除 pytest 配置中的覆盖率和并行选项(调试器兼容性)
- 将 lambda 改为命名函数 _candidate_key,提供清晰的类型提示
- 统一使用 pathlib.Path 对象处理路径,移除 os.path 依赖
- 更新 find_candidates 返回类型为 list[tuple[Path, list[str], str]]
- 更新相关测试断言以匹配 Path 类型
- 简化 get_inner_items_list 使用 list() 替代不必要的列表推导式
Remove unused jump_to_end_of_arg method and fix empty directory logic:
- When encountering an empty directory during separator matching, the search should fail (break) instead of adding the empty directory as a candidate
- Add variable new_work_path for better readability
- Update test_empty_directory and test_file_skipped to reflect the corrected behavior
Replace unittest.mock.patch with pyfakefs for filesystem operations:
- Use fs.create_dir() and fs.create_file() for test fixtures
- Remove unnecessary check_platform mocks (Windows-only tests skip non-Windows)
- Fix lambda signatures for DesktopIniHandler static methods
- Split test_interactive_mode_scenarios into three separate tests
- Use pytest.MonkeyPatch.context() for runtime mocking

All 139 tests pass successfully.
Add UTF-8 encoding enforcement in pytest_configure hook to handle
emoji and special characters (like ❤) in test output on terminals
with default GBK encoding. This ensures tests pass regardless of
the terminal's default encoding setting.
Remove pull_request.yml which was using outdated Python versions (3.9, 3.10).
The test.yml workflow now handles both push and pull_request events with the
correct Python version matrix (3.11, 3.12, 3.13).

This fixes the failing CI checks on PRs caused by the old workflow testing
unsupported Python versions.
Use PyInstaller's OPTIONS mechanism to enable UTF-8 mode, which is the
correct way to handle encoding in frozen executables. Environment variables
like PYTHONUTF8 do not work in packaged exe files.

- Add ('X utf8', None, 'OPTION') to EXE configuration in remark.spec
- Remove push trigger from test.yml to avoid duplicate CI runs (only PR triggers needed)
@piratf piratf merged commit 2971952 into main Jan 25, 2026
4 checks passed
@piratf piratf deleted the feat-lint-test branch January 25, 2026 13:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant